package com.six.backend.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.six.backend.config.QiNiuCloudProperties;
import com.six.backend.dao.MaterialDao;
import com.six.backend.dao.fileChunkRecordDao;
import com.six.backend.dao.fileRecordDao;
import com.six.backend.pojo.*;
import com.six.backend.service.SourceMaterialService;
import com.six.backend.utils.QiNiuUtils;
import com.six.backend.utils.TokenUtil;
import com.sun.javafx.scene.shape.PathUtils;
import jdk.nashorn.internal.parser.Token;
import org.springframework.util.ResourceUtils;
import sun.misc.Cleaner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Author: skyler
 * @Date: 2021/11/10 11:01
 */
@Service
public class SourceMaterialServiceImpl implements SourceMaterialService {
    @Autowired
    private MaterialDao materialDao;

    @Autowired
    private fileChunkRecordDao fileChunkRecordDao;

    @Autowired
    private fileRecordDao fileRecordDao;

    @Autowired
    private QiNiuCloudProperties qiNiuCloudProperties;

    @Value("E:/project/upload")
    private String FILE_UPLOAD_DIR;

    @Value("10485760")
    private Integer CHUNK_SIZE;

    /**
     * 第几页
     */
    static String PAGE_NUM = "pageNum";
    /**
     * 页面大小
     */
    static String PAGE_SIZE ="pageSize";

    @Override
    public List<SourceMaterial> selectImgSourceMaterialByName(HashMap map,String account) {
        if (map.get(PAGE_NUM) != null && map.get(PAGE_SIZE) != null) {
            int pageNum = Integer.parseInt(map.get("pageNum").toString());
            int pageSize = Integer.parseInt(map.get("pageSize").toString());
            PageHelper.startPage(pageNum, pageSize);
        }
        List<SourceMaterial> sourceMaterials = materialDao.selectImgSourceMaterialByName(map);
        return sourceMaterials;
    }

    @Override
    public List<SourceMaterial> selectVedioSourceMaterialByName(HashMap map) {
        List<SourceMaterial> sourceMaterials = materialDao.selectVedioSourceMaterialByName(map);
        return sourceMaterials;
    }

    @Override
    public List<SourceMaterial> selectDocumentSourceMaterialByName(Map map) {
        if (map.get(PAGE_NUM) != null && map.get(PAGE_SIZE) != null) {
            int pageNum = Integer.parseInt(map.get("pageNum").toString());
            int pageSize = Integer.parseInt(map.get("pageSize").toString());
            PageHelper.startPage(pageNum, pageSize);
        }
        List<SourceMaterial> documentSourceMaterialByName = materialDao.selectDocumentSourceMaterialByName(map);
        return documentSourceMaterialByName;
    }

    @Override
    public String deleteDocumentByName(HashMap map,String id) throws FileNotFoundException {
        String status = QiNiuUtils.delete(qiNiuCloudProperties,id);
        if ("true".equals(status)) {
            Integer integer = materialDao.deleteSourceMaterial(map);
            if (integer > 0) {
                return "true";
            }
            return "false";
        }
        return "false";
    }

    @Override
    public String save(SourceMaterial sourceMaterial,MultipartFile uploadFile) throws IOException {
        String oldFileName = uploadFile.getOriginalFilename();
        String type = uploadFile.getContentType().startsWith("image")?"图片":"文档";
        sourceMaterial.setName(oldFileName);
        sourceMaterial.setType(type);
        sourceMaterial.setSource("本地上传");
        sourceMaterial.setImportTime(new Date());
        materialDao.save(sourceMaterial);
        String id = sourceMaterial.getId();
        return id;
    }

    @Override
    public SourceMaterial selectSourceById(HashMap map) {
        return materialDao.selectMaterialById(map);
    }

    @Override
    public String chunkUpload(MultipartFileParam multipartFileParam, HttpServletResponse response, HttpServletRequest request,String auther,Integer conferenceId) throws IOException {
        /**
         * 每一个上传块都会包含如下分块信息：
         * chunkNumber: 当前块的次序，第一个块是 1，注意不是从 0 开始的。
         * totalChunks: 文件被分成块的总数。
         * chunkSize: 分块大小，根据 totalSize 和这个值你就可以计算出总共的块数。注意最后一块的大小可能会比这个要大。
         * currentChunkSize: 当前块的大小，实际大小。
         * totalSize: 文件总大小。
         * identifier: 这个就是每个文件的唯一标示。
         * fileName: 文件名。
         * relativePath: 文件夹上传的时候文件的相对路径属性。
         * 一个分块可以被上传多次，当然这肯定不是标准行为，但是在实际上传过程中是可能发生这种事情的，这种重传也是本库的特性之一。
         *
         * 根据响应码认为成功或失败的：
         * 200 文件上传完成
         * 201 文加快上传成功
         * 500 第一块上传失败，取消整个文件上传
         * 507 服务器出错自动重试该文件块上传
         */
        //处理文件存储目录
        String realPath1 = ResourceUtils.getURL("classpath:").getPath() + "static/files/" + multipartFileParam.getFileName();
        String realPath = URLDecoder.decode(realPath1,"utf-8");
        File dateDir = new File(realPath);
        if (!dateDir.getParentFile().exists()) {
            dateDir.getParentFile().mkdirs();
        }
        //进行写文件操作
        boolean finished = multipartFileParam.getChunkNumber() == multipartFileParam.getTotalTrunk();
        try {
            RandomAccessFile accessFile = new RandomAccessFile(realPath,"rw");
            if (finished) {
                //移动指针到指定位置
                accessFile.seek(multipartFileParam.getTotalSize() - multipartFileParam.getFile().getSize());
            }else {
                accessFile.seek((multipartFileParam.getChunkNumber()-1) * multipartFileParam.getFile().getSize());
            }
            accessFile.write(multipartFileParam.getFile().getBytes());
            accessFile.close();
        }catch (Exception e) {
            e.printStackTrace();
            return "false";
        }


        if(finished){
            // TODO 向数据库中保存上传信息
            //获取文件名
            String domain = "http://skylery.walnute.top/";
            String key = QiNiuUtils.localChunkUpload(realPath,multipartFileParam.getFileName());
            String url =  domain+ key +"?attname="+multipartFileParam.getFileName();
            String account = TokenUtil.getTokenName(request);
            SourceMaterial sourceMaterial = new SourceMaterial();
            sourceMaterial.setId(key);
            sourceMaterial.setName(multipartFileParam.getFileName());
            sourceMaterial.setType("多媒体");
            if (auther != null) {
                sourceMaterial.setAuthor(auther);
            }
            sourceMaterial.setSource("本地上传");
            sourceMaterial.setImportPeople(account);
            sourceMaterial.setImportTime(new Date());
            sourceMaterial.setBelongConferenceId(conferenceId);
            sourceMaterial.setAddress(url);
            materialDao.save(sourceMaterial);
            return url;
        }
        return "true";
    }

    @Override
    public boolean saveVideoSource(SourceMaterial sourceMaterial) {
        sourceMaterial.setType("多媒体");
        Integer num =  materialDao.save(sourceMaterial);
        return num > 0;
    }

    @Override
    public List<PreviewData> getPreviewData(String account, String name) {
        return materialDao.getPreviewData(account,name);
    }

    @Override
    public String checkSource(String id,Integer conferneceId) {
        return materialDao.checkSource(id,conferneceId);
    }

    private boolean renameFile(File tempFile, String fileName) {
        //检查要重命名的文件是否存在，是否是文件
        if (!tempFile.exists() || tempFile.isDirectory()) {
            return false;
        }
        String parentPath = tempFile.getParent();
        File newFile = new File(parentPath + File.separatorChar + fileName);
        if (newFile.exists()) {
            newFile.delete();
        }
        return tempFile.renameTo(newFile);
    }

    private void cleanUp(File tempFile, String fileMd5) {
        if (tempFile.exists()) {
            tempFile.delete();
        }
        //删除上传记录
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("md5",fileMd5);
        fileChunkRecordDao.delete(queryWrapper);
    }

    private boolean checkFileMd5(File tempFile, String fileMd5) throws IOException {
        FileInputStream fis = new FileInputStream(tempFile);
        String checkMD5 = DigestUtils.md5DigestAsHex(fis).toUpperCase();
        fis.close();
        if (checkMD5.equals(fileMd5.toUpperCase())) {
            return true;
        }
        return false;
    }

    private File tempFile(String parentDir, String tempFileName,
                          MultipartFile multipartFile, int currentChunk,
                          int totalChunk, long totalSize, String fileMd5) throws IOException {
        System.out.println("开始上传文件，时间是:" + new Date() + ",文件名称是:" + tempFileName);
        long position = (currentChunk - 1) * CHUNK_SIZE;
        File tmpDir = new File(parentDir);
        File TmpFile = new File(parentDir,tempFileName);
        if (!tmpDir.exists()) {
            tmpDir.mkdirs();
        }

        RandomAccessFile tempRaf = new RandomAccessFile(TmpFile,"rw");
        if (tempRaf.length() == 0) {
            tempRaf.setLength(totalSize);
        }

        //写入该分片数据
        FileChannel fc = tempRaf.getChannel();
        MappedByteBuffer map = fc.map(FileChannel.MapMode.READ_WRITE,position, multipartFile.getSize());
        map.put(multipartFile.getBytes());
        clean(map);
        fc.close();
        tempRaf.close();

        //记录以及上传完成的分片
        FileChunkRecord fileChunkRecord = new FileChunkRecord();
        fileChunkRecord.setMd5(fileMd5).setUploadStatus(1).setChunk(currentChunk);
        fileChunkRecordDao.insert(fileChunkRecord);
        System.out.println("上传文件完成，时间是:" + new Date() + ",文件名称是:" + tempFileName);

        return TmpFile;
    }

    private void clean(MappedByteBuffer map) {
        try {
            Method getCleanerMethod = map.getClass().getMethod("cleaner");
            Cleaner.create(map,null);
            getCleanerMethod.setAccessible(true);
            Cleaner cleaner = (Cleaner) getCleanerMethod.invoke(map);
            cleaner.clean();
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
